// Ofri Keidar 302933981 group 05

/**
 * Base class for all derived apartments.
 * Used as a common interface and implements methods that run the same
 * for all classes. The class is serializable so a building could be saved and restored from a file easily
 */

package model.apartment;

import java.io.Serializable;
import java.lang.String;
import java.util.HashMap;
import java.util.Map;

public abstract class Apartment implements Serializable, Comparable<Apartment> {
	
	/**
	 * This class is used to extract parameters out of tokens string
	 */
	public static class BasicExtractor implements ParameterParser {
		
		// minimal number of parameters
		private static final int MIN_PARAMETER_NUM = 4; 
		
		/**
		 * Gets an array of tokens and verifies that parameters are:
		 * integer value for floor and double-format values for number of rooms, size and extra unit.
		 * If resident name is given, it is extracted.
		 * All extracted parameters are stored in the map. 
		 * If format is invalid, an empty map is returned 
		 */
		public Map<String, String> parseParameters(String[] parameters) {
		
			// initialize parameter map
			Map<String, String> parameterMap = new HashMap<String, String>();
			
			// check if enough parameters received
			if (parameters.length < MIN_PARAMETER_NUM) {
				return parameterMap;
			}
			
			// check parameters format
			try {				

				// check if value given for floor is a positive integer value
				double checkNumericValue = Integer.parseInt(parameters[FLOOR_INDX]);
				if (checkNumericValue <= 0) {
					// floor cannot be non-positive
					return new HashMap<String, String>();
				}				
				// value is correct, update parameter map
				parameterMap.put(FLOOR, parameters[FLOOR_INDX]);
				
				// check if value given for room number is a positive numeric value
				checkNumericValue = Double.parseDouble(parameters[ROOM_NUMBER_INDX]);
				if (checkNumericValue <= 0) {
					// room number cannot be non-positive
					return new HashMap<String, String>();
				}
				// value is correct, update parameter map
				parameterMap.put(ROOM_NUMBER, parameters[ROOM_NUMBER_INDX]);
				
				// check if value given for size is a positive numeric value
				checkNumericValue = Double.parseDouble(parameters[SIZE_INDX]);
				if (checkNumericValue <= 0) {
					// apartment size cannot be non-positive
					return new HashMap<String, String>();
				}
				// value is correct, update parameter map
				parameterMap.put(SIZE, parameters[SIZE_INDX]);
				
				// check if value given for extra unit is a non-negative numeric value
				checkNumericValue = Double.parseDouble(parameters[EXTRA_UNIT_INDX]);
				if (checkNumericValue < 0) {
					// extra unit size cannot be negative
					return new HashMap<String, String>();
				}
				// value is correct, update parameter map
				parameterMap.put(EXTRA_UNIT, parameters[EXTRA_UNIT_INDX]);
				
				// initialize resident name
				String resName = "";
				// if resident name is given
				if (parameters.length >= MIN_PARAMETER_NUM + 1) {
					// get resident name
					resName = parameters[RES_NAME_INDX];
				}
				// update parameter map
				parameterMap.put(RESIDENT_NAME, resName);
				
				// return map holding parameters
				return parameterMap;
			
			} catch (NumberFormatException e) {
				// at least one of numeric parameters was not inserted correctly
				return new HashMap<String, String>();
			}
		}
	}

	private static final long serialVersionUID = 7812933978976638366L; // set serial number

	// members
	private int floor; // floor's number
	private double roomNum; // room number
	private double size; // size in square-meters
	private double extraUnit; // extra unit of each apartment 
	private String resName; // resident's name
	
	// constants for compareTo methods
	private static final int LOWER = -1; 
	private static final int EQUAL = 0;
	private static final int GREATER = 1;
	

	// methods
	
	/**
	 * Constructor- initializes a new apartment according to given values
	 * @param floor- apartment's floor
	 * @param roomNum- number of rooms
	 * @param size- aparment's size
	 * @param extraUnit- extra unit's area
	 * @param resName- resident name
	 */
	public Apartment(int floor, double roomNum, double size, double extraUnit ,String resName) {
		// initialize class attributes according to given values 
		this.floor = floor;
		this.roomNum = roomNum;
		this.size = size;
		this.extraUnit = extraUnit;
		this.resName = resName;
	}
	
	/**
	 * Constructor- used when no resident name is given
	 * @param floor- apartment's floor
	 * @param roomNum- number of rooms
	 * @param size- aparment's size
	 * @param extraUnit- extra unit's area
	 */
	public Apartment(int floor, double roomNum, double size, double extraUnit) {
		// initialize class attributes according to given values 
		this.floor = floor;
		this.roomNum = roomNum;
		this.size = size;
		this.extraUnit = extraUnit;
		this.resName = ""; // no resident name given
	} 

	/**
	 * Returns apartment's floor number
	 * @return floor number attribute
	 */
	public int getFloor() { return floor; }

	/**
	 * Returns number of rooms in the apartment
	 * @return room number attribute
	 */
	public double getRoomNum() { return roomNum; }

	/**
	 * Sets apartment's room number as given value
	 * @param newNum- new room number
	 */
	public void setRoomNum(double newNum) { roomNum = newNum; }
	
	/**
	 * Returns apartment's size
	 * @return size attribute
	 */
	public double getSize() { return size; }

	/**
	 * Returns apartment's resident name
	 * @return resident name attribute
	 */
	public String getName() { return resName; }
	
	/**
	 * Sets apartment's resident name as given name
	 * @param newName- new resident name
	 */
	public void setName(String newName) { resName = newName; }
	
	/**
	 * Returns area of apartment's extra unit
	 * @return- extra unit area attribute
	 */
	double getExtraUnit() { return extraUnit; }
	
	/**
	 * Sets new extra unit's value as given value
	 * @param newUnitArea- new area for extra unit
	 */
	void setExtraUnit(double newUnitArea) { extraUnit = newUnitArea; }

	/**
	 * Returns apartment's total area. Value is calculated according to derived class
	 * @return apartment's total area
	 */
	public abstract double totalArea();
	
	/**
	 * Checks if apartment is vacant
	 * @return- True if apartment is vacant, False if not
	 */
	public boolean isVacant() {
		
		// check if there is a resident
		if (resName.equals("")) {
			// no resident, apartment is vacant
			return true;
		}
		
		// apartment is not vacant
		return false;
	}
	
	/**
	 * Compares activating object to given apartment and returns result
	 * @param otherAprmnt- apartment to be compared to
	 * @return -1 if activating object's area is smaller than given apartment's, 0 if areas are equal or 1 if activating object's area is bigger than given apartment's  
	 */
	public int compareTo(Apartment otherAprmnt) {

		// compare both apartments' areas
		
		if (totalArea() < otherAprmnt.totalArea()) {
			// activating object is smaller than given apartment
			return LOWER;
		} else if (totalArea() == otherAprmnt.totalArea()) {
			// activating object is equal to given apartment
			return EQUAL;
		}
		
		// activating object is bigger than given apartment
		return GREATER;

	}
	
	/**
	 * Returns the most basic printing format string that is common for all derived classes
	 */
	public String toString() {
		// return basic output string
		return "Room #: " + roomNum + ", Total area: " + totalArea();		
	}

}